-- luajit classinfo.lua input.class

local error = error
local arg = arg
local string = string
local format = string.format
local concat = table.concat
local io = io
local write = io.write

local f = io.open(arg[1], "rb")
if not f then
	error("can not open file:" .. arg[1])
	return
end
local s = f:read "*a"
f:close()

local pos = 0

local function printinfo(...)
	write(format("%06X: ", pos), format(...), "\n")
end

local function dumpbin(n)
	local t = {}
	for i = 1, n do
		t[i] = format(i < n and "%02X " or "%02X", s:byte(pos + i))
	end
	return concat(t)
end

local function read1()
	return s:byte(pos + 1)
end

local function read2be()
	return s:byte(pos + 1) * 0x100 + s:byte(pos + 2)
end

local function read4be()
	return s:byte(pos + 1) * 0x1000000 + s:byte(pos + 2) * 0x10000 + s:byte(pos + 3) * 0x100 + s:byte(pos + 4)
end

printinfo("magic[4]: %s", dumpbin(4)); pos = pos + 4 -- always CA FE BA BE
printinfo("minor_version[2]: %d", read2be()); pos = pos + 2 -- almost 0
printinfo("major_version[2]: %d", read2be()); pos = pos + 2 -- Java4/5/6/7/8: 48/49/50/51/52

local n = read2be()
printinfo("constant_pool_count[2]: %d(-1)", n); pos = pos + 2
local const = {}
local i = 1; while i < n do
	--	CONSTANT_Utf8_info					1	len[2] + bytes[len](utf-8)
	--	CONSTANT_Integer_info				3	v[4]
	--	CONSTANT_Float_info					4	v[4]
	--	CONSTANT_Long_info					5	v[8] (2 slots)
	--	CONSTANT_Double_info				6	v[8] (2 slots)
	--	CONSTANT_Class_info					7	index[2]			包含类或者接口全限定名的CONSTANT_Utf8_info表的索引
	--	CONSTANT_String_info				8	index[2]			包含文字字符串值的CONSTANT_Utf8_info表的索引
	--	CONSTANT_Fieldref_info				9	index[2] + index[2]	声明被引用字段的类或者接口的CONSTANT_Class_info入口的索引	提供了CONSTANT_NameAndType_info入口的索引，该入口提供了字段的简单名称以及描述符
	--	CONSTANT_Methodref_info				10	index[2] + index[2]	声明被引用方法的类的CONSTANT_Class_info入口的索引			提供了CONSTANT_NameAndType_info入口的索引，该入口提供了方法的简单名称以及描述符
	--	CONSTANT_InterfaceMethodref_info	11	index[2] + index[2]	声明被引用方法的接口的CONSTANT_Class_info入口的索引			提供了CONSTANT_NameAndType_info入口的索引，该入口提供了方法的简单名称以及描述符
	--	CONSTANT_NameAndType_info			12	index[2] + index[2]	给出了CONSTANT_Utf8_info入口的索引，该入口给出了字段或者方法的名称	提供了CONSTANT_Utf8_info入口的索引，该入口提供了字段或者方法的描述符
	--	CONSTANT_MethodHandle_info			15	type[1] + index[2]
	--	CONSTANT_MethodType_info			16	index[2]
	--	CONSTANT_Dynamic_info				17	index[2] + index[2]
	--	CONSTANT_InvokeDynamic_info			18	index[2] + index[2]
	--	CONSTANT_Module_info				19	index[2]
	--	CONSTANT_Package_info				20	index[2]
	local oldPos = pos
	local t = read1(); pos = pos + 1
	if t == 1 then
		local len = read2be(); pos = pos + 2 + len
		const[i] = s:sub(pos - len + 1, pos)
	elseif t == 7 or t == 8 or t == 16 or t == 19 or t == 20 then
		const[i] = read2be(); pos = pos + 2
	elseif t == 15 then
		local a = read1(); pos = pos + 1
		local b = read2be(); pos = pos + 2
		const[i] = format("%d, %d", a, b)
	elseif t == 5 or t == 6 then
		const[i] = dumpbin(8); pos = pos + 8
	else
		const[i] = read4be(); pos = pos + 4
	end
	local newPos = pos
	pos = oldPos
	printinfo("  [%5d] (%2d) %s", i, t, const[i])
	pos = newPos
	i = i + ((t == 5 or t == 6) and 2 or 1)
end

--	ACC_PUBLIC			0x0001	pubilc
--	ACC_PRIVATE			0x0002	private
--	ACC_PROTECTED		0x0004	protected
--	ACC_STATIC			0x0008	static
--	ACC_FINAL			0x0010	final
--	ACC_SUPER			0x0020	用于兼容早期编译器, 新编译器都设置该标记, 以在使用 invokespecial指令时对子类方法做特定处理。
--	ACC_BRIDGE			0x0040	bridge方法, 由编译器生成。
--	ACC_INTERFACE		0x0200	接口, 同时需要设置：ACC_ABSTRACT。不可同时设置：ACC_FINAL、ACC_SUPER、ACC_ENUM
--	ACC_ABSTRACT		0x0400	abstract
--	ACC_SYNTHETIC		0x1000	synthetic, 由编译器产生, 不存在于源代码中。
--	ACC_ANNOTATION		0x2000	注解类型（annotation）, 需同时设置：ACC_INTERFACE、ACC_ABSTRACT
--	ACC_ENUM			0x4000	枚举类型
printinfo("access_flags[2]: 0x%04X", read2be(2)); pos = pos + 2
printinfo("this_class[2]: %d", read2be()); pos = pos + 2
printinfo("super_class[2]: %d", read2be()); pos = pos + 2

printinfo("interfaces_count[2]: %d", read2be()); pos = pos + 2
--TODO

--	u2	access_flag
--		ACC_VOLATILE	0x0040	volatile
--		ACC_TRANSIENT	0x0080	transient
--	u2	name_index			提供了给出字段简单名称（不是全限定名）的CONSTANT_Utf8_info入口的索引
--	u2	descriptor_index	提供了给出字段描述符的CONSTANT_Utf8_info入口的索引
--	u2	attributes_count
--	attribute_info	attribute_info[attributes_count]
--		u2	attribute_name_index
--		u4	attribute_length
--		u1	bytes[attribute_length]
n = read2be(); pos = pos + 2
printinfo("fields_count[2]: %d", n)
for i = 1, n do
	printinfo(" -access_flags[2]: 0x%04X", read2be(2)); pos = pos + 2
	printinfo("  name_index[2]: %d", read2be()); pos = pos + 2
	printinfo("  descriptor_index[2]: %d", read2be()); pos = pos + 2
	local m = read2be(); pos = pos + 2
	printinfo("  attributes_count[2]: %d", m)
	if m > 0 then printinfo("  attribute_info:") end
	for j = 1, m do
		printinfo("   -attribute_name_index[2]: %d", read2be()); pos = pos + 2
		local k = read4be(); pos = pos + 4
		printinfo("    attribute_length[4]: %d", k)
		printinfo("    info[%d]: %s", k, dumpbin(k)); pos = pos + k
	end
end

--	u2	access_flag
--		ACC_SYNCHRONIZED0x0020	synchronized
--		ACC_VARARGS		0x0080	包含不定参数个数的方法。
--		ACC_NATIVE		0x0100	native
--		ACC_STRICT		0x0800	strictfp
--	u2	name_index			提供了给出字段简单名称（不是全限定名）的CONSTANT_Utf8_info入口的索引
--	u2	descriptor_index	提供了给出字段描述符的CONSTANT_Utf8_info入口的索引
--	u2	attributes_count
--	attribute_info	attribute_info[attributes_count]
--		u2	attribute_name_index
--		u4	attribute_length
--		u1	bytes[attribute_length]
n = read2be(); pos = pos + 2
printinfo("methods_count[2]: %d", n)
for i = 1, n do
	printinfo(" -access_flags[2]: 0x%04X", read2be(2)); pos = pos + 2
	printinfo("  name_index[2]: %d", read2be()); pos = pos + 2
	printinfo("  descriptor_index[2]: %d", read2be()); pos = pos + 2
	local m = read2be(); pos = pos + 2
	printinfo("  attributes_count[2]: %d", m)
	if m > 0 then printinfo("  attribute_info:") end
	for j = 1, m do
		printinfo("   -attribute_name_index[2]: %d", read2be()); pos = pos + 2
		local k = read4be(); pos = pos + 4
		printinfo("    attribute_length[4]: %d", k)
		printinfo("    info[%d]: %s", k, dumpbin(k)); pos = pos + k
	end
end

--	u2	attribute_name_index	给出了包含属性名称的CONSTANT_Utf8入口的常量池中的索引
--	u4	attribute_length		给出了属性数据的长度（以字节计）
--	u1	info[attribute_length]	包含属性数据
n = read2be();  pos = pos + 2
printinfo("attribute_count[2]: %d", n)
if n > 0 then printinfo("attribute_info:") end
for i = 1, n do
	local j = read2be(); pos = pos + 2
	printinfo(" -attribute_name_index[2]: %d : %s", j, const[j])
	local m = read4be(); pos = pos + 4
	printinfo("  attribute_length[4]: %d", m)
	printinfo("  info[%d]: %s", m, dumpbin(m)); pos = pos + m
end

printinfo("<END>")
